package icu.lingjie.灵阵;

import icu.lingjie.灵界.灵界类;
import static icu.lingjie.util.ConfigUtil.*;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@Slf4j
public class 灵阵类 {

    private 灵界类 灵界;

    /**
     * 只能解析小于1的版本号
     */
    private static long 版本号 = 1;

    /**
     * 文件开头
     * "LINGZHEN".split("").map((c,i)=>c.charCodeAt().toString(16)).join("").toUpperCase();
     */
    public static final Long LINGZHEN = Long.parseLong("4C494E475A48454E", 16);

    /**
     * id : 阵盘类
     */
    private Map<Long, 阵盘类> 阵盘们;

    /**
     * 世界时间
     */
    private final AtomicLong 世界时间 = new AtomicLong();
    public long get世界时间() {
        return 世界时间.get();
    }
    public void set世界时间(long 时间戳) {
        世界时间.set(时间戳);
    }

    /**
     * 每个小格 力fx,fy, fz   其他信息
     * 大小       4,4,  4      4
     * 顺组       0,1,  2      3
     */
    private byte[][] 影响力数组;

    /**
     * 使用的哪个影响力数组
     * 影响力数据有两套，方便刷新地图，不保存到文件，省事儿
     */
    private byte[] 当前的影响力数组;
    private byte[] 下一个影响力数组;
    public byte[] get当前的影响力数组(){
        return this.当前的影响力数组;
    }
    private void 交换灵界数组(){
        byte[] tmp = 当前的影响力数组;
        当前的影响力数组 = 下一个影响力数组;
        下一个影响力数组 = tmp;
    }

    /**
     * 初始化灵阵
     */
    public volatile boolean 初始化好了吗 = false;

    public void 初始化(灵界类 灵界) throws Exception{
        this.灵界 = 灵界;
        int size = 灵界长度X * 灵界宽度Y * 灵界高度Z * 8;
        log.info("初始化灵阵数组");
        影响力数组 = new byte[2][size];
        阵盘们 = new ConcurrentHashMap<>(4096);
        加载灵阵文件();
        log.info("初始化灵阵数组成功");
        初始化好了吗 = true;
    }

    /**
     * 加载灵阵文件
     *
     * 1. LINGZHEN, 文件开头
     * 2. 版本号， long, 只能解析版本为1的
     * 3. 世界时间
     * 9. skip(256-8-8-8)
     *
     * // 阵盘数据
     *
     * 1. id, long , 小于等于0代表输入结束
     * 2. 世界时间, long, 世界时间
     * 3. 灵力, long, 灵力
     * 4. 阵盘大小, int, 阵盘大小，现在只有几个固定的值（1M,2M,4M,8M...），以后再说其他的
     * 9. skip(256-8-8-8-45)
     * 10. 阵盘数据
     */
    private void 加载灵阵文件() throws Exception{
        log.info("加载灵阵文件, 灵阵文件={}", 灵阵文件);
        File file = new File(灵阵文件);
        if (!file.exists()) {
            log.error("灵阵文件不存在!,{}",灵阵文件);
            if (autoCreateFile) {
                阵盘们.put(1L, 阵盘类.builder().id(1).灵力(1111).符文数组(new int[(1<<21)>>2]).阵盘大小(1<<21).build());
                保存灵阵文件();
            } else {
                throw new Exception("灵阵文件不存在(" + 灵阵文件 + ")");
            }
        }
        try(InputStream fileInputStream = new FileInputStream(file);
            DataInputStream in = new DataInputStream(fileInputStream)) {
            // 1. LINGZHEN
            final long head = in.readLong();
            if (head != LINGZHEN){
                throw new Exception("文件类型错误，不是灵阵文件，文件：（"+灵阵文件+"）");
            }
            // 2. 版本号
            long version = in.readLong();
            if (version != 版本号) {
                throw new Exception("灵阵文件版本号为"+ version +"，解析不了。只能解析版本号为" + 版本号 + "的。文件：（"+灵阵文件+"）");
            }
            // 3. 世界时间
            this.世界时间.set(in.readLong());
            this.当前的影响力数组 = this.影响力数组[(int)(世界时间.get()&1)];
            this.下一个影响力数组 = this.影响力数组[1-(int)(世界时间.get()&1)];
            // 9. skip
            in.skipBytes(256-8-8-8);
            Map<Integer, byte[]> byteMap = new HashMap<>();
            for (int count=0; true; count++) {
                try{
                    // 1. id
                    long id = in.readLong();
                    if (id <= 0){
                        log.info("读取灵阵文件结束, 共读取{}个",count);
                        break;
                    }
                    // 2. 世界时间
                    long time = in.readLong();
                    // 3. 灵力
                    long lingLi = in.readLong();
                    // 4. 阵盘大小
                    int size = (int)in.readLong();
                    // 9. skip
                    in.skipBytes(256-8-8-8-8);
                    // 10. 阵盘数据
                    byte[] bytes = byteMap.computeIfAbsent(size, key->new byte[size]);
                    int[] ints = new int[size/4];
                    in.read(bytes);
                    for (int i = 0; i < bytes.length; i+=4) {
                        ints[i>>2] = ((bytes[i]&0xFF)<<24) | ((bytes[i+1]&0xFF)<<16) | ((bytes[i+2]&0xFF)<<8) | ((bytes[i+3]&0xFF));
                    }
                    final 阵盘类 阵盘 = 阵盘类.builder().id(id).time(time).灵力(lingLi).阵盘大小(size).符文数组(ints).build();
                    this.阵盘们.put(id, 阵盘);
                } catch (EOFException eofException) {
                    log.info("读取灵阵文件结束, count={}, e={}", count, eofException);
                    break;
                }
            }
            log.info("加载灵阵文件成功,灵阵文件={}",灵阵文件);
        }
    }

    /**
     * 保存灵阵文件
     */
    private void 保存灵阵文件(){
        log.info("保存灵阵文件");
        File file = new File(灵阵文件);
        if (!file.exists()) {
            log.error("灵阵文件不存在={}",灵阵文件);
            if (!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }
        } else {
            String dateTime = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(System.currentTimeMillis());
            file.renameTo(new File(灵阵文件+dateTime));
            file = new File(灵阵文件);
        }
        try(FileOutputStream outputStream = new FileOutputStream(file);
            DataOutputStream out = new DataOutputStream(outputStream)) {
            // 1. LINGZHEN
            out.writeLong(LINGZHEN);
            // 2. 版本号
            out.writeLong(版本号);
            // 3. 世界时间
            out.writeLong(this.世界时间.get());
            // 9. skip
            out.write(new byte[256-8-8-8]);
            final byte[] skipBytes = new byte[256-8-8-8-8];
            Map<Integer, byte[]> byteMap = new HashMap<>();
            for (阵盘类 zp : this.阵盘们.values()) {
                // 1. id
                out.writeLong(zp.getId());
                // 2. 世界时间
                out.writeLong(zp.getTime());
                // 3. 灵力
                out.writeLong(zp.get灵力());
                // 4. 阵盘大小
                final int size = zp.get阵盘大小();
                out.writeLong(size);
                // 9. skip
                out.write(skipBytes);
                // 10. 阵盘数据
                byte[] bytes = byteMap.computeIfAbsent(size, key->new byte[size]);
                int[] ints = zp.get符文数组();
                for (int i = 0; i < bytes.length; ) {
                    bytes[i] = (byte)((ints[i>>2]>>>24) & 0xFF);
                    i++;
                    bytes[i] = (byte)((ints[i>>2]>>>16) & 0xFF);
                    i++;
                    bytes[i] = (byte)((ints[i>>2]>>>8) & 0xFF);
                    i++;
                    bytes[i] = (byte)((ints[i>>2]) & 0xFF);
                    i++;
                }
                out.write(bytes);
            }
            // 11. 写入0表示文件结束
            out.write(new byte[256]);

            log.info("保存灵阵文件成功,灵阵文件={}",灵阵文件);
        } catch (Exception e) {
            log.error("保存灵阵文件失败,灵阵文件={},{}",灵阵文件,e);
        }
    }

    /**
     * 获取运行的方法
     */
    public Runnable[] 获取运行的方法(){
        Runnable[] 刷新的方法 = new Runnable[线程数];
        int 每个方法的X长度 = 灵界长度X / 线程数;
        for (int i = 0; i < 刷新的方法.length; i++) {
            刷新的方法[i] = () -> {
                世界时间.compareAndSet(世界时间.get(), 灵界.get世界时间());
            };
        }
        return 刷新的方法;
    }


    /**
     * 其他
     * 注意：这儿的力折合成加速度去存储
     * 每个小格   ax,ay, az   其他信息
     * 大小       4,4,  4      4      共16位,a有正负
     */
    public int getAX(int x, int y, int z){
        return 0;
//        return (当前的影响力数组[idx] >>> 4) - 0b111;
    }
    public int getAY(int x, int y, int z){
        return 0;
//        return (当前的影响力数组[idx] & 0xF) - 0b111;
    }
    public int getAZ(int x, int y, int z){
        return 0;
//        return (当前的影响力数组[idx+1]>>>4) - 0b111;
    }
    public int getT(int idx){
        return (当前的影响力数组[idx+1] & 0xF);
    }
    public void setA(int px, int py, int pz, int ax, int ay, int az, int t){
        下一个影响力数组[getIdx(px, py, pz)]    = (byte)((ax <<4) | (ay &0xF));
        下一个影响力数组[getIdx(px, py, pz)]  = (byte)((az <<4) | (t&0xF));
    }
    public void clearA(int x, int y, int z) {
        int idx = getIdx(x, y, z);
        下一个影响力数组[idx] = 0;
        下一个影响力数组[idx+1] = 0;
    }
    // 计算idx
    public int getIdx(int x,int y,int z){
        return (((x+灵界长度X)%灵界长度X)*Y乘Z + ((y+灵界宽度Y)%灵界宽度Y)*灵界高度Z + ((z+灵界高度Z)%灵界高度Z))<<2;
    }



//    private byte[] byte4 = new byte[4];
//    private int byte4ToInt() {
//        return ((byte4[0]&0xFF)<<24) + ((byte4[1]&0xFF)<<16) + ((byte4[2]&0xFF)<<8) + (byte4[3]&0xFF);
//    }
//    private byte[] intToByte4(int value) {
//        return new byte[]{ (byte)((value>>24) & 0xFF), (byte)((value>>16) & 0xFF), (byte)((value>>8) & 0xFF), (byte)((value) & 0xFF) };
//    }
//    阵盘们.put(1L, 阵盘类.builder().id(1).灵力(1111).符文数组(new int[(1<<21)>>2]).阵盘大小(1<<21).世界时间(1).build())
//

}
